package controller;

import javafx.application.Platform;
import javafx.fxml.FXML;
import javafx.fxml.FXMLLoader;
import javafx.fxml.Initializable;
import javafx.scene.Cursor;
import javafx.scene.Node;
import javafx.scene.Parent;
import javafx.scene.Scene;
import javafx.scene.canvas.Canvas;
import javafx.scene.canvas.GraphicsContext;
import javafx.scene.control.*;
import javafx.scene.control.Button;
import javafx.scene.control.Label;
import javafx.scene.control.MenuItem;
import javafx.scene.control.TextField;
import javafx.scene.effect.Bloom;
import javafx.scene.image.Image;
import javafx.scene.image.ImageView;
import javafx.scene.image.PixelFormat;
import javafx.scene.image.WritablePixelFormat;
import javafx.scene.input.Dragboard;
import javafx.scene.input.KeyCode;
import javafx.scene.input.MouseEvent;
import javafx.scene.input.TransferMode;
import javafx.scene.layout.AnchorPane;
import javafx.scene.paint.Color;
import javafx.scene.shape.SVGPath;
import javafx.scene.text.Text;
import javafx.stage.Stage;
import javafx.stage.StageStyle;
import ui.DialogUtils;
import ui.SVGFiles;
import util.NodeDragUtil;

import java.awt.*;
import java.io.File;
import java.io.IOException;
import java.net.URL;
import java.nio.ByteBuffer;
import java.util.*;
import java.util.List;

public class BuilderPane implements Initializable {
    /*Right Map Pane*/
    @FXML
    Canvas canvas_mesh;
    @FXML
    AnchorPane ap_drag;
    @FXML
    ImageView iv_pic;

    /*Left Tree Pane*/
    @FXML
    AnchorPane treePane;
    @FXML
    TreeView<String> tree;

    /*Information Pane*/
    @FXML
    Label lb_node_name;
    @FXML
    Label lb_file_info;
    @FXML
    Label lb_pic_info;

    /*Operation Pane*/
    @FXML
    Button btn_zoom_in;
    @FXML
    Button btn_zoom_out;
    @FXML
    Button btn_base;
    @FXML
    Button btn_camera;
    @FXML
    Label lb_position;
    @FXML
    Label test_info;

    private Stage primaryStage;
    private Stage mapStage;
    private double zoomSize = 1;
    private String PATH = "";

    private List<String> picType = new ArrayList<>(Arrays.asList(".jpg", ".png", ".gif", ".jpeg"));
    @Override
    public void initialize(URL location, ResourceBundle resources) {
        TreeItem<String> rootItem = new TreeItem<>("一级地图",SVGFiles.getPath(SVGFiles.SPLIT));
        rootItem.setExpanded(true);
        tree.setRoot(rootItem);
        TreeItem<String> item = new TreeItem<>("二级地图A",SVGFiles.getPath(SVGFiles.SPLIT));
        rootItem.getChildren().add(item);
        item = new TreeItem<>("二级地图B",SVGFiles.getPath(SVGFiles.SPLIT));
        TreeItem<String> item2 = new TreeItem<>("三级地图A",SVGFiles.getPath(SVGFiles.SPLIT));
        item.getChildren().add(item2);
        item2 = new TreeItem<>("三级地图B",SVGFiles.getPath(SVGFiles.SPLIT));
        item.getChildren().add(item2);
        TreeItem<String> item3 = new TreeItem<>("四级地图A",SVGFiles.getPath(SVGFiles.SPLIT));
        item2.getChildren().add(item3);
        item3 = new TreeItem<>("四级地图B",SVGFiles.getPath(SVGFiles.SPLIT));
        item2.getChildren().add(item3);
        item3 = new TreeItem<>("四级地图C",SVGFiles.getPath(SVGFiles.SPLIT));
        item2.getChildren().add(item3);
        rootItem.getChildren().add(item);
        tree.setEditable(true);
        tree.setCellFactory(param -> new TextFieldTreeCellImpl());
        tree.addEventFilter(MouseEvent.MOUSE_CLICKED, event -> {
            primaryStage = (Stage) treePane.getScene().getWindow();
            Node node = event.getPickResult().getIntersectedNode();
            if (node instanceof Text || (node instanceof TreeCell && ((TreeCell) node).getText() != null)) {
                String name = (String) ((TreeItem) tree.getSelectionModel().getSelectedItem()).getValue();
                Platform.runLater(()->{
                    lb_node_name.setText(name);
                });
            }
        });
        initMapStage();
    }

    @FXML
    private void openFilePath() throws IOException {
        if (PATH != null && !PATH.equals("")) {
            Desktop.getDesktop().open(new File(PATH));
        }
    }
    @FXML
    private void zoomIn(){
        zoom(1.05);
    }
    @FXML
    private void zoomOut(){
        zoom(0.95);
    }
    List<SVGPath> bases = new ArrayList<>();
    @FXML
    private void addBase(){
        if(iv_pic.getImage()!=null){
            AnchorPane anchorPane = new AnchorPane();
            anchorPane.setStyle("-fx-border-color: #00000080;-fx-border-width: 0.5;");
            SVGPath base = SVGFiles.getPath(SVGFiles.BASE);
            //base.set
            double x = iv_pic.getLayoutX() + 20 * ap_drag.getChildren().size();
            double y = iv_pic.getLayoutY() + 20 * ap_drag.getChildren().size();
            anchorPane.setTranslateX(x);
            anchorPane.setTranslateY(y);
            base.setEffect(new Bloom(0.2));
            base.setId("svg_base_"+bases.size());
            anchorPane.translateXProperty().addListener((observable, oldValue, newValue) -> {
                setIconFill(anchorPane,base);
            });
            anchorPane.getChildren().add(base);
            NodeDragUtil.addNodeDragListener(anchorPane,ap_drag);
            ap_drag.getChildren().add(anchorPane);
        }
    }
    private void setIconFill(AnchorPane anchorPane,SVGPath icon){
        double x = anchorPane.getTranslateX()+iv_pic.getTranslateX();
        double y = anchorPane.getTranslateY()+iv_pic.getTranslateY();
        Platform.runLater(()->{
            test_info.setText("a X():"+anchorPane.getTranslateX()+"\n"+"a Y():"+anchorPane.getTranslateY()+
                    "i X():"+iv_pic.getTranslateX()+"\n"+"i Y():"+iv_pic.getTranslateY());
        });
        WritablePixelFormat<ByteBuffer> writablePixelFormat = PixelFormat.getByteBgraPreInstance();
        byte[] bytes = new byte[30*30*4];
        iv_pic.getImage().getPixelReader().getPixels((int)x,(int)y,30,30,writablePixelFormat,bytes,0,30*4);
        int aa = 0,rr=0,gg=0,bb = 0;
        for(int i = 0 ;i< bytes.length;i = i + 4 ){
            int b = bytes[i] & 0xff;
            int g = bytes[i + 1] & 0xff;
            int r = bytes[i + 2] & 0xff;
            int a = bytes[i + 3] & 0xff;
            aa += a;
            rr += r;
            gg += g;
            bb += b;
        }
        icon.setFill(new Color( (float)(255-rr/900)/255 , (float)(255-gg/900)/255, (float)(255-bb/900)/255 , (float)(aa/900)/255) );
    }
    @FXML
    private void setZero(){
        iv_pic.setTranslateX(0);
        iv_pic.setTranslateY(0);
    }

    private void zoom(double zoomFactor){
        zoomSize *= zoomFactor;
        if(zoomSize < 4 && zoomSize > 0.8 && iv_pic.getImage()!=null){
            iv_pic.setFitHeight(iv_pic.getFitHeight() * zoomFactor);
            iv_pic.setFitWidth(iv_pic.getFitWidth() * zoomFactor);
        }else {
            zoomSize = zoomSize>4 ? 4 : 0.8;
        }
    }
    private void showXY(){
        Platform.runLater(()->{
            lb_position.setText("X: "+iv_pic.getTranslateX()+" Y: "+iv_pic.getTranslateY());
        });
    }


    private void initMapStage(){
        iv_pic.setPreserveRatio(false);
        drawMesh();
        ap_drag.setOnDragOver(event -> {
            if (event.getGestureSource() != ap_drag
                    && event.getDragboard().hasFiles()) {
                /* allow for both copying and moving, whatever user chooses */
                event.acceptTransferModes(TransferMode.COPY_OR_MOVE);
            }
            event.consume();
        });
        NodeDragUtil.addNodeDragListener(iv_pic,ap_drag);
        iv_pic.setCursor(Cursor.CLOSED_HAND);
        iv_pic.translateXProperty().addListener((observable, oldValue, newValue) -> {
            showXY();
        });
        iv_pic.translateYProperty().addListener((observable, oldValue, newValue) -> {
            showXY();
        });
        ap_drag.widthProperty().addListener((observable, oldValue, newValue) -> {
            if(iv_pic.getImage()!=null){
                /*double pic_w = iv_pic.getImage().getWidth();
                double pic_h = iv_pic.getImage().getHeight();
                double pane_w = newValue.doubleValue() - 20;
                iv_pic.setFitWidth(pane_w);
                iv_pic.setFitHeight(pane_w*pic_h/pic_w);*/
            }
        });
        iv_pic.setOnScroll(event -> {
            double zoomFactor = 1.05;
            double deltaY = event.getDeltaY();
            if (deltaY < 0){
                zoomFactor = 0.95;
            }
            zoom(zoomFactor);
            event.consume();
        });
        /*文件拖拽事件*/
        ap_drag.setOnDragDropped(event -> {
            Dragboard db = event.getDragboard();
            boolean success = false;
            if (db.hasFiles()) {
                success = true;
                String filePath = db.getFiles().get(0).getPath();
                String name = db.getFiles().get(0).getName();
                PATH = filePath.replace(name,"");
                if(picType.contains("."+db.getFiles().get(0).getName().split("\\.")[1])){
                    Image img = new Image("file:/" + filePath);
                    iv_pic.setFitWidth(780);
                    iv_pic.setFitHeight(780*img.getHeight()/img.getWidth());
                    iv_pic.setImage(img);
                    Platform.runLater(()->{
                        lb_file_info.setText(filePath);
                        lb_pic_info.setText(img.getWidth()+" * "+img.getHeight());
                    });
                }else {
                    new DialogUtils(new DialogUtils.Builder()
                            .setTitle("失败")
                            .setContent("暂不支持导入该格式！")
                            .setAlertType(Alert.AlertType.INFORMATION))
                            .showAndWait();
                }

            }
            event.setDropCompleted(success);

            event.consume();
        });
        ap_drag.widthProperty().addListener((observable, oldValue, newValue) -> {
            drawMesh();
        });
        ap_drag.heightProperty().addListener((observable, oldValue, newValue) -> {
            drawMesh();
        });
    }
    private void mapStage(String name){
        if(mapStage==null){
            try {
                mapStage = new Stage();
                Parent root = FXMLLoader.load(getClass().getResource("/main.fxml"));
                Scene mapScene = new Scene(root,670,670);
                mapStage.setScene(mapScene);
                mapStage.initStyle(StageStyle.TRANSPARENT);
            }catch (Exception e){
                e.printStackTrace();
            }
        }
        mapStage.setTitle(name);
        initMapStage();
        mapStage.show();
    }

    /*canvas 绘制网格*/
    private void drawMesh(){
        //canvas_mesh = new Canvas(canvas_mesh.getWidth(), canvas_mesh.getHeight());
        double width, height, rows = 30, columns = 30;/*定义行、列*/
        /*获取父节点的长宽*/
        width = ap_drag.widthProperty().doubleValue();
        height = ap_drag.heightProperty().doubleValue();
        canvas_mesh.setWidth(width);
        canvas_mesh.setHeight(height);
        GraphicsContext gc = canvas_mesh.getGraphicsContext2D();
        gc.clearRect(0, 0, width, height);//清除已经绘制的网格
        gc.setFill(Color.WHITE);
        gc.setStroke(Color.BLACK);
        gc.setLineWidth(0.05);/*绘制线重*/
        int k;
        int htOfRow = (int) (height / rows);/*行高*/
        for (k = 0; k < rows; k++) {
            gc.strokeLine(0, k * htOfRow, width, k * htOfRow);
        }

        int wdOfRow = (int) (width / columns);/*列高*/
        for (k = 0; k < columns; k++) {
            gc.strokeLine(k * wdOfRow, 0, k * wdOfRow, height);
        }
    }

    private void map(){
        mapStage.maximizedProperty().addListener((observable, oldValue, newValue) -> {
            primaryStage.setAlwaysOnTop(newValue);
            primaryStage.setX(0);
            primaryStage.setY(0);
            primaryStage.show();
        });
        //mapStage.fullScreenProperty().addListener((observable, oldValue, newValue) -> primaryStage.setAlwaysOnTop(newValue));
        primaryStage.xProperty().addListener((observable, oldValue, newValue) -> {
            setXY();
        });
        primaryStage.yProperty().addListener((observable, oldValue, newValue) -> {
            setXY();
        });
        if(!mapStage.isMaximized()&&!mapStage.isFullScreen()){
            setXY();
        }else {
            primaryStage.setX(0);
            primaryStage.setY(0);
            primaryStage.show();
        }
        /*最小化事件*/
        mapStage.iconifiedProperty().addListener((observable, oldValue, newValue) -> {
            mapStage.setIconified(false);
        });
        /*关闭事件*/
        primaryStage.setOnCloseRequest(event -> {
            System.exit(0);
        });
    }

    /*设置XY*/
    private void setXY(){
        mapStage.setX(primaryStage.getX()+treePane.widthProperty().getValue()+treePane.getTranslateX());
        mapStage.setY(primaryStage.getY());
    }

    private static final class TextFieldTreeCellImpl extends TreeCell<String> {
        private TextField textField;
        private ContextMenu addMenu = new ContextMenu();

        public TextFieldTreeCellImpl() {
            MenuItem edit = new MenuItem("重命名");
            MenuItem addMenuItem = new MenuItem("添加节点");
            MenuItem delete = new MenuItem("删除节点");
            edit.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("/img/dialog/edit.png"))));
            addMenuItem.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("/img/dialog/add.png"))));
            delete.setGraphic(new ImageView(new Image(getClass().getResourceAsStream("/img/dialog/delete.png"))));
            addMenu.getItems().add(edit);
            addMenu.getItems().add(addMenuItem);
            addMenu.getItems().add(delete);
            edit.setOnAction(event -> {
                editStart();
            });
            addMenuItem.setOnAction(event -> {
                TreeItem<String> newNode = new TreeItem<>("新节点",SVGFiles.getPath(SVGFiles.SPLIT));
                getTreeItem().getChildren().add(newNode);
                getTreeItem().setExpanded(true);
            });
            delete.setOnAction(event -> {
                if(getTreeItem().getParent()!=null){
                    getTreeItem().getParent().getChildren().remove(getTreeItem());
                }else {
                    new DialogUtils(new DialogUtils.Builder()
                            .setTitle("错误")
                            .setContent("最上层节点不能删除")
                            .setAlertType(Alert.AlertType.ERROR))
                            .showAndWait();
                }
            });
        }

        //重写编辑事件 使原鼠标点击事件失效
        @Override
        public void startEdit() {
            System.out.println("单击");
        }
        //真·编辑事件
        void editStart(){
            super.startEdit();
            createTextField();
            setText(null);
            setGraphic(textField);
            textField.selectAll();
        }
        //编辑取消
        @Override
        public void cancelEdit() {
            super.cancelEdit();
            setText(getItem());
            setGraphic(getTreeItem().getGraphic());
        }
        //编辑
        @Override
        public void updateItem(String item, boolean empty) {
            super.updateItem(item, empty);
            if (empty) {
                setText(null);
                setGraphic(null);
            } else {
                setText(getString());
                setGraphic(getTreeItem().getGraphic());
                setContextMenu(addMenu);
            }
        }

        private void createTextField() {
            textField = new TextField(getString());
            textField.setOnKeyReleased(t -> {
                if (t.getCode() == KeyCode.ENTER) {
                    if(!getString().equals("新节点")){
                        DialogUtils dialog = new DialogUtils(new DialogUtils.Builder()
                                .setAlertType(Alert.AlertType.NONE)
                                .setTitle("确认修改吗??")
                                .setHeadText("将 " + getString() + " 改成 " + textField.getText())
                                .setButtonTypes(
                                        new ButtonType("取消", ButtonBar.ButtonData.CANCEL_CLOSE),
                                        new ButtonType("否", ButtonBar.ButtonData.NO),
                                        new ButtonType("是", ButtonBar.ButtonData.YES))
                        );
                        Optional<ButtonType> btn = dialog.showAndWait();
                        if (btn.get().getButtonData() == ButtonBar.ButtonData.YES) {
                            commitEdit(textField.getText());
                        } else {
                            cancelEdit();
                        }
                    }else {
                        commitEdit(textField.getText());
                    }
                } else if (t.getCode() == KeyCode.ESCAPE) {
                    cancelEdit();
                }
            });

        }

        private String getString() {
            return getItem() == null ? "" : getItem();
        }
    }

    /*private static final class MyTreeItem extends TreeItem{
        public MyTreeItem(Object value) {
            super(value,SVGFiles.getPath(SVGFiles.SPLIT));
        }
    }*/
}
